﻿// Copyright 2013 Ronald Schlenker, www.technewlogic.de.
// 
// This file is part of Byond.
// 
// Byond is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 2 of the License, or
// (at your option) any later version.
// 
// Byond is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with Byond.  If not, see <http://www.gnu.org/licenses/>.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Byond.EnsembleLibrary;
using Byond.Messages;
using Utils.Messaging;

namespace Byond
{
	public sealed class EnsembleManager : IEnsembleQueryService
	{
		private readonly EnsembleSerializer _serializer;
		private readonly Func<string, Ensemble> _creator;
		private readonly Library _library;
		private readonly List<Ensemble> _createdEnsembles = new List<Ensemble>();

		public event MessageEventHandler SendMessage;

		private Ensemble _editingEnsemble;
		public Ensemble EditingEnsemble
		{
			get { return _editingEnsemble; }
			private set
			{
				_editingEnsemble = value;
				SendMessage(new EnsembleShownMessage());
			}
		}

		//private SaveResult CheckSaveEnsemble()
		//{
		//    if (EditingEnsemble != null && EditingEnsemble.IsNew)
		//    {
		//        var res = SaveAs(EditingEnsemble, true);
		//        return res;
		//    }
		//    return SaveResult.GoOn;
		//}

		private void RefreshEnsembles(LibraryFolder libraryFolder)
		{
			var allItems = new List<LibraryItem>();
			GetAllLibraryItems(libraryFolder, allItems);

			// remove old ensembles
			_createdEnsembles
				.Where(it => !it.IsNew)
				.Where(it => allItems.All(it2 => it2.Path != it.FileName))
				.ToList()
				.ForEach(it => _createdEnsembles.Remove(it));

			// add new ones
			allItems
				.Where(it => _createdEnsembles.All(it2 => it2.FileName != it.Path))
				.ForEach(it => Create(it.Path));
		}

		private static void GetAllLibraryItems(LibraryFolder folder, List<LibraryItem> items)
		{
			items.AddRange(folder.Items);
			folder.Folders.ForEach(it => GetAllLibraryItems(it, items));
		}

		public void HandleMessage(LibraryUpdatedMessage message)
		{
			RefreshEnsembles(_library.CreateRootFolder());
		}

		//public void HandleMessage(SaveAllEnsemblesRequest request)
		//{
		//    SaveAll();
		//}

		//public void HandleMessage(SaveEnsembleAsRequest request)
		//{
		//    SaveAs(EditingEnsemble, false);
		//}

		//public void HandleMessage(ActivateEnsembleRequest request)
		//{
		//    if (CheckSaveEnsemble() == SaveResult.Cancel)
		//        return;
		//    EditingEnsemble = request.Ensemble;
		//    ActiveEnsemble = request.Ensemble;
		//}

		//public void HandleMessage(ShowEnsembleRequest request)
		//{
		//    if (CheckSaveEnsemble() == SaveResult.Cancel)
		//        return;
		//    EditingEnsemble = request.Ensemble;
		//}

		public void HandleMessage(DeleteEnsembleRequest request)
		{
			Delete(request);
		}

		public void HandleMessage(NewEnsembleRequest request)
		{
			Create();
		}

		//private void SaveAll()
		//{
		//    if (EditingEnsemble != null && EditingEnsemble.IsNew)
		//        SaveAs(EditingEnsemble, false);

		//    Ensembles
		//        .Where(it => it.IsDirty)
		//        .Where(it => !it.IsNew)
		//        .ForEach(it => Save(it, it.FileName));
		//}

		//private SaveResult SaveAs(Ensemble ensemble, bool askForSave)
		//{
		//    var dialog = _createSaveDialog();
		//    var res = askForSave ? dialog.SaveAsAndAsk() : dialog.SaveAs();
		//    var fileName = dialog.FileName;
		//    if (res == SaveResult.Save)
		//        Save(ensemble, fileName);
		//    return res;
		//}

		private void Save(Ensemble ensemble, string fileName)
		{
			_serializer.Serialize(ensemble, fileName);
			if (ensemble.IsNew)
				ensemble.FileName = fileName;
			_uow.MarkClean(ensemble);
			SendMessage(new EnsemblesUpdatedMessage());
		}

		private void Create()
		{
			var ensemble = _creator(string.Empty);
			_createdEnsembles.Add(ensemble);
			ensemble.ChangeTrackingEnabled = true;
			SendMessage(new ActivateEnsembleRequest(null));
			SendMessage(new ShowEnsembleRequest(ensemble));
		}

		private void Create(string fileName)
		{
			var ensemble = _createdEnsembles.FirstOrDefault(it => it.FileName == fileName);
			// TODO: Hier Exception, wenn es einen Fehler gibt? (hängt auch mit EBVM.ToViewModel() zusammen)
			if (ensemble != null)
				return;
			ensemble = _creator(fileName);
			_serializer.Deserialize(ensemble);
			ensemble.ChangeTrackingEnabled = true;
			_createdEnsembles.Add(ensemble);
		}

		private void Delete(DeleteEnsembleRequest request)
		{
			File.Delete(request.Ensemble.FileName);
			_uow.MarkClean(request.Ensemble);
			_createdEnsembles.Remove(request.Ensemble);
			if (EditingEnsemble == request.Ensemble)
				EditingEnsemble = null;
			SendMessage(new EnsemblesUpdatedMessage());
		}

		public Ensemble TryGetByFileName(string fileName)
		{
			return _createdEnsembles.FirstOrDefault(it2 =>
				it2.FileName.Equals(fileName, StringComparison.InvariantCultureIgnoreCase));
		}

		public Ensemble GetByFileName(string fileName)
		{
			var ensemble = TryGetByFileName(fileName);
			if (ensemble == null)
				throw new Exception("No ensemble found for filename " + fileName);
			return ensemble;
		}
	}
}
